﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using BookmarksDb.Data;

namespace BookmarksComplexSearch
{
    public class ComplexSearchManager
    {
        public static void Main()
        {
            string xmlPath = @"../../../../Resources/complex-query.xml";
            string xmlDestinationPath = @"../../../../Resources/search-results.xml";
            Encoding encoding = Encoding.GetEncoding("windows-1251");

            SearchAndGenerateXmlResultsFile(xmlPath, xmlDestinationPath, encoding);
        }

        private static void SearchAndGenerateXmlResultsFile(string xmlPath, string xmlDestinationPath, Encoding encoding)
        {
            XmlReader reader = XmlReader.Create(xmlPath);

            using (reader)
            {
                XmlTextWriter writer = new XmlTextWriter(xmlDestinationPath, encoding);

                using (writer)
                {
                    writer.Formatting = Formatting.Indented;
                    writer.IndentChar = ' ';
                    writer.Indentation = 4;

                    writer.WriteStartDocument();
                    writer.WriteStartElement("search-results");

                    bool hasQuery = false;
                    string username = string.Empty;
                    List<string> allTags = new List<string>();
                    int maxResults = 10;

                    while (reader.Read())
                    {
                        if (reader.Name == "query" && hasQuery)
                        {
                            List<Bookmark> bookmarks = ExecuteDatabaseSearch(username, allTags, maxResults);
                            InsertResultIntoXml(writer, bookmarks);

                            ResetValues(ref username, ref allTags, ref maxResults);
                            hasQuery = false;
                        }
                        else if (reader.Name == "query" && !hasQuery)
                        {
                            if (reader.HasAttributes)
                            {
                                maxResults = int.Parse(reader.GetAttribute(0));
                            }

                            if (reader.IsEmptyElement)
                            {
                                List<Bookmark> bookmarks = ExecuteDatabaseSearch(username, allTags, maxResults);
                                InsertResultIntoXml(writer, bookmarks);

                                ResetValues(ref username, ref allTags, ref maxResults);
                                hasQuery = false;
                            }

                            hasQuery = true;
                        }
                        else if (reader.Name == "username")
                        {
                            username = reader.ReadElementString();
                            hasQuery = true;
                        }
                        else if (reader.Name == "tag")
                        {
                            allTags.Add(reader.ReadElementString());
                            hasQuery = true;
                        }
                    }

                    writer.WriteEndDocument();
                }
            }
        }

        private static List<Bookmark> ExecuteDatabaseSearch(string username, List<string> allTags, int maxResults)
        {
            using (var context = new BookmarksDBEntities())
            {
                var resultBookmarks = context.Bookmarks.Include("User").Include("Tags").AsQueryable();

                if (username != string.Empty)
                {
                    resultBookmarks = resultBookmarks.Where(x => x.User.Username == username);
                }

                if (allTags.Count != 0)
                {
                    foreach (var tag in allTags)
                    {
                        resultBookmarks = resultBookmarks.Where(x => x.Tags.Any(t => t.Name == tag));
                    }
                }

                List<Bookmark> result = resultBookmarks.OrderBy(x => x.URL).Take(maxResults).ToList();

                return result;
            }
        }

        private static void InsertResultIntoXml(XmlTextWriter writer, List<Bookmark> bookmarks)
        {
            writer.WriteStartElement("result-set");

            if (bookmarks.Count != 0)
            {
                foreach (var bookmark in bookmarks)
                {
                    writer.WriteStartElement("bookmark");

                    if (bookmark.User.Username != string.Empty)
                    {
                        writer.WriteElementString("username", bookmark.User.Username);
                    }

                    if (bookmark.Title != string.Empty)
                    {
                        writer.WriteElementString("title", bookmark.Title);
                    }

                    if (bookmark.URL != string.Empty)
                    {
                        writer.WriteElementString("url", bookmark.URL);
                    }

                    if (bookmark.Tags.Count != 0)
                    {
                        string allTags = string.Join(", ", bookmark.Tags.Select(x => x.Name).OrderBy(x => x));
                        writer.WriteElementString("tags", allTags);
                    }

                    if (bookmark.Notes != string.Empty)
                    {
                        writer.WriteElementString("notes", bookmark.Notes);
                    }

                    writer.WriteEndElement();
                }
            }

            writer.WriteEndElement();
        }

        private static void ResetValues(ref string username, ref List<string> allTags, ref int maxResults)
        {
            username = string.Empty;
            allTags.Clear();
            maxResults = 10;
        }
    }
}
